package langnstats.project.languagemodel.loglinear;

///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2001 Chieu Hai Leong and Jason Baldridge
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//////////////////////////////////////////////////////////////////////////////   

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;

import langnstats.project.lib.LanguageModel;
import langnstats.project.lib.WordType;
import langnstats.project.lib.crossvalidation.TrainTokens;
import opennlp.maxent.BasicContextGenerator;
import opennlp.maxent.BasicEventStream;
import opennlp.maxent.ContextGenerator;
import opennlp.maxent.EventStream;
import opennlp.maxent.GIS;
import opennlp.maxent.GISModel;
import opennlp.maxent.PlainTextByLineDataStream;
import opennlp.maxent.io.GISModelReader;
import opennlp.maxent.io.GISModelWriter;
import opennlp.maxent.io.SuffixSensitiveGISModelReader;
import opennlp.maxent.io.SuffixSensitiveGISModelWriter;

/**
 * Main class which calls the GIS procedure after building the EventStream
 * from the data.
 *
 * @author  Chieu Hai Leong and Jason Baldridge
 * @version $Revision: 1.5 $, $Date: 2005/10/24 12:29:20 $
 */
public class LogLinear implements LanguageModel {
	
	private static final long serialVersionUID = 99L;

	// some parameters if you want to play around with the smoothing option
	// for model training.  This can improve model accuracy, though training
	// will potentially take longer and use more memory.  Model size will also
	// be larger.  Initial testing indicates improvements for models built on
	// small data sets and few outcomes, but performance degradation for those
	// with large data sets and lots of outcomes.
	public static boolean USE_SMOOTHING = true;
	public static int CUT_OFF = 3 ;
	public static int ITERATIONS = 150;
	public static double SMOOTHING_OBSERVATION = 0.5;
	
	private static GISModel model=null; 
	private String modelContent = "";
	public static ContextGenerator _cg = new BasicContextGenerator();

	public LanguageModel clone() {
		return new LogLinear(this);
	}

	public LogLinear() {
	}

	public LogLinear(LogLinear c) {
	}
	String modelFileName = "/tmp/ModelFile";
	public void prepare(WordType[] allWordtype) {
	}

	public double[] predict(WordType wordType) {
		if(LLFeature.getFeatures().size()<1){
			LLFeature.addFeature(new BigramFeature());
			LLFeature.addFeature(new BigramCluster());
			LLFeature.addFeature(new TrigramFeature());
			LLFeature.addFeature(new TrigramCluster());
			LLFeature.addFeature(new TrigramSemiCluster());
			LLFeature.addFeature(new Position());
			LLFeature.addFeature(new FirstToken());
			LLFeature.addFeature(new ExistVerb());
			LLFeature.addFeature(new ExistPunc());
			LLFeature.addFeature(new ParseFeature());
		}			
			double[] ocs;


			String featureStr = "";

			
			if(wordType == null){
				featureStr="";
			}
			else{
				featureStr += LLFeature.buildFeatureString(wordType);
//	 			LLFeature.rewindAll();
			}
			
//			LLFeature.removeAllFeatures();
			if(model==null){
				try {
					FileWriter wr = new FileWriter(modelFileName);
					wr.write(modelContent);
					wr.close();
					GISModelReader rd = new SuffixSensitiveGISModelReader(new File(modelFileName));
					model = rd.getModel();
				} catch (IOException e) {
					
					e.printStackTrace();
				}
			}
			ocs = model.eval(_cg.getContext(featureStr));

			double[] sorted = new double[WordType.size()];
			for (int i = 0; i < WordType.size(); i++) {
				String oc = model.getOutcome(i);
				if(oc.length()>4){
					oc="<"+oc+">";
				}
				WordType wt = WordType.get(oc);
				sorted[wt.getIndex()] = ocs[i];
			}
			return sorted;
		

	}

	public String getDescription() {
		return "Log-Linear Model";
	}

	public void train(TrainTokens trainTokens) {
		System.err.println("Training Language Model" + getDescription());
		LLFeature.rewindAll();

		WordType[] tt = trainTokens.getTokenArray();
		
		StringWriter strWriter = null;

		strWriter = new StringWriter();
		PrintWriter outputStream = new PrintWriter(strWriter);

		String featureStr = "";
		
		if(LLFeature.getFeatures().size()<1){
			LLFeature.addFeature(new BigramFeature());
			LLFeature.addFeature(new BigramCluster());
			LLFeature.addFeature(new TrigramFeature());
			LLFeature.addFeature(new TrigramCluster());
			LLFeature.addFeature(new TrigramSemiCluster());
			LLFeature.addFeature(new Position());
			LLFeature.addFeature(new FirstToken());
			LLFeature.addFeature(new ExistVerb());
			LLFeature.addFeature(new ExistPunc());
			LLFeature.addFeature(new ParseFeature());
		}
		
		for (int i = 0; i < tt.length; i++) {
			if (i == 0) {
				featureStr = "";
			}
			else{
				featureStr += LLFeature.buildFeatureString(tt[i-1]);
//				LLFeature.rewindAll();
			}

			featureStr += " " + tt[i].getName();  // outcome

			outputStream.println(featureStr);
			featureStr = "";
		}
		
		LLFeature.removeAllFeatures();

		try {
			StringReader datafr = new StringReader(strWriter.toString());

			EventStream es = new BasicEventStream(
					new PlainTextByLineDataStream(datafr));
			GIS.SMOOTHING_OBSERVATION = SMOOTHING_OBSERVATION;
			model = GIS.trainModel(es, ITERATIONS, CUT_OFF, USE_SMOOTHING, true);

			File outputFile = new File(modelFileName);
			GISModelWriter writer = new SuffixSensitiveGISModelWriter(model,
					outputFile);
			writer.persist();
			BufferedReader rd = new BufferedReader(new FileReader(outputFile.getAbsolutePath()));
			String st = "";
			while((st=rd.readLine())!=null){
				modelContent += st + "\n";
			}
		} catch (Exception e) {
			System.out.print("Unable to create model due to exception: ");
			System.out.println(e);
		}
	}

}
